import { parsePath } from "../util/lang"
import { nextTick } from "../util/next-tick"
import { isFunction } from "../util/util"
import { popTarget, pushTarget } from "./dep"
let id = 0
// 实现实时更新
class Watcher {
  constructor(vm, expOrFn, cb, options) {
    this.vm = vm
    this.expOrFn = expOrFn
    this.cb = cb // 回调函数
    this.options = options
    this.lazy = options.lazy // 如果这个watcher 上的lazy为true 说明他是计算属性
    this.dirty = this.lazy // 用户是否执行
    this.id = id++
    this.deps = [] // watcher 存放dep
    this.depId = new Set()
    // 可能为undefined
    this.user = !!options.user
    // 判断当前的updateComponent是不是一个方法
    if (isFunction(expOrFn)) {
      this.getter = expOrFn // 用来更新视图的
    }else{
      // 字符串走这里  将字符串变成函数
      // 这里的情况可能要监听c.c.c这个数据 所以要进行循环先
      // watch初始化走这
      // this.getter = parsePath(expOrFn)
      // console.log('[ this.getter ] >', this.getter())
      this.getter = function(){
        let obj = vm
        const segments = expOrFn.split('.')
        for (let i = 0; i < segments.length; i++) {
          if (!obj) return
          obj = obj[segments[i]]
        }
        return obj
      }
      // if (!this.getter) {
      //   this.getter = noop
      //   __DEV__ &&
      //     warn(
      //       `Failed watching path: "${expOrFn}" ` +
      //         'Watcher only accepts simple dot-delimited paths. ' +
      //         'For full control, use a function instead.',
      //       vm
      //     )
      // }
    }
    // 类里面的方法自调用
    // this.value 新值进行保存
    //页面渲染
    // void 0  任何都不执行 如果是计算属性就不执行
    this.value = this.lazy?void 0 : this.get()
    // console.log('[ this.value ] >', this.value)
  }
  // 初次渲染
  // 先把watch添加到里面 然后执行渲染完毕后在删除
  get() {
    //
    let value
    pushTarget(this)
    // 旧值进行保存
    value = this.getter.call(this.vm) //指向问题
    popTarget()
    // console.log('[ get ] >', value)
    // 把第一次的值返回出去
    return value
  }
  addDep(dep) {
    // 去重
    // this.depId
    let id = dep.id
    if (!this.depId.has(id)) {
      this.deps.push(dep)
      this.depId.add(id)
      dep.addSub(this)
    }
  }
  //更新
  update() {
    // 数据更新不会马上执行 用异步更新 
    // this.getter()
    if(this.lazy){
      this.dirty = true
    }else{
      queueWatcher(this)
    }
    
  }
  evaluate(){
    this.value = this.get()
    this.dirty = false
  }
  // 相互收集
  depend(){
    //收集watcher 存放到dep中
    let i = this.deps.length
    while(i--){
      this.deps[i].depend()
    }
  }
  // 数据改变才会走到这
  run() {
    // 更新后走到这这就是一个新值

    let value = this.getter()
    let oldValue = this.value
    // 在把新值赋值过去
    this.value = value
    // console.log('[ value,oldValue ] >', value,oldValue)
    // 执行cb 这是用户的watch 如果user为真表示这是用户的
    if(this.user){
      // 给用户返回新值和旧值
      this.cb.call(this.vm,value,oldValue)
    }
  }

}

// 将需要批量更新的watcher放到这个数组
let queue = []
let has = {}
let pending = false
function flushWatcher() {
    queue.forEach((item)=>{
      // console.log('[ item ] >', item)
      item.run()
      // item.cb()
    })
    queue = []
    has = {}
    pending = false 
}
function queueWatcher(watcher) {
  const id = watcher.id
  // 使用防抖
  if (has[id] === undefined) {
    queue.push(watcher)
    has[id] = true
    if (!pending) {
      // setTimeout(()=>{
      //   queue.forEach((item)=>{
      //     item.run()
      //   })
      //   queue = []
      //   has = {}
      //   pending = false
      // },0)
      nextTick(flushWatcher)
    }
    pending = true
  }

}
export default Watcher

// 收集依赖 dep watcher 面试题
// dep和data中的属性是一一对应的
// watcher在页面上用了几个就有几个watcher
// dep与watcher 现在是一对多 其实是多对多
// 多对多就是dep包含watcher  watcher包含dep
// 数组更新
//  1.给数组的所有对象都增加一个dep
//  2.获取数组的值 会调用get 我们希望让当前的数组记住这个渲染的watcher
//    2.1 获取到当前dep  2.2 当我们的数组面对数组取值的时候就让dep记住这个watcher 2.3等待更新